﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using  PI_CS_Library.PicsNetwork_Information_Model;
using  PI_CS_Library.Virtual_Network_Information_Model;
using PI_CS_Tools;
using TransportLayer.Manager;
using System.ComponentModel;


namespace  PI_CS_Library
{
    public class PicsNetworkController 
    {
        PicsNetwork _network;
        Dictionary <int, VirtualNetworkOperatorInfo> _VNoperators;
        SubnetworkConnectionController _SNCC;
        RoutingController _RC;
        Engine _managerEngine;
        string _configFile;

        public Engine ManagerEngine { get { return _managerEngine; } }

        public PicsNetworkController()
        {
            _network = new PicsNetwork();
            _VNoperators = new Dictionary<int, VirtualNetworkOperatorInfo>();
            _managerEngine = new Engine();
            _SNCC = new SubnetworkConnectionController(_network, _managerEngine);
            _RC = new RoutingController(_network);
        }

        public bool InitiateTopology(string _filename)
        {
            _managerEngine.Init();
            Loader _topologyLoader = new Loader(_managerEngine);
            _topologyLoader.Show();
            if (!_topologyLoader.LoadNetwork(_filename))
            {
                return false;
            }

            foreach (Engine.Node n in _managerEngine.Nodes.Values)
            {
                MatrixParameters _mParameters = new MatrixParameters(n.Accessible, n.Delay);  

                _network.AddNewMatrix(n.Id, n.Name, n.Type, n.PositionX, n.PositionY, _mParameters);

            }

            foreach (Engine.Link l in _managerEngine.Links.Values)
            {
                LinkParameters _lParameters = new LinkParameters(l.Capacity, l.Delay, l.ErrorRate);
                PicsMatrix _srcMatrix = _network.GetMatrix(l.SourceNode.Id);
                PicsMatrix _sinkMatrix = _network.GetMatrix(l.TargetNode.Id);

                _network.AddNewLink(l.Id, _srcMatrix, _sinkMatrix, l.SourcePort, l.TargetPort, _lParameters);
            }

            _configFile = _filename;

            return true;

        }

        public PicsNetwork Network
        {
            get { return _network; }
            set { _network = value; }
        }

        public SubnetworkConnectionController SNCC
        {
            get { return _SNCC; }
            set { _SNCC = value; }
        }

        public RoutingController RC
        {
            get { return _RC; }
            set { _RC = value; }
        }

        public Dictionary<int, VirtualNetworkOperatorInfo> VNOperators
        {
            get { return _VNoperators; }
            set { _VNoperators = value; }
        }

        public string ConfigFile
        {
            get { return _configFile; }
            set { _configFile = value; }
        }

        public void AddVNO(VirtualNetworkOperatorInfo _newVNO)
        {
            _VNoperators.Add(_newVNO.Id.Number, _newVNO);
        }

        public VirtualNetworkOperatorInfo GetVNO(int _vnoId)
        {
            return _VNoperators[_vnoId];
        }
        
        public Dictionary<int, Dictionary<int, PicsLink>> MapVirtualLinks(Dictionary<int, Link> _virtualLinkList)
        {
            Dictionary<int, Dictionary<int,PicsLink>> _allLinkList = new Dictionary<int, Dictionary<int, PicsLink>>();

            foreach (Link l in _virtualLinkList.Values)
            {
                int _id = _network.LinkMappingList[l.Id.Number];

                PicsVirtualLink _vPicsLink = _network.VirtualLinkList[_id];

                _allLinkList.Add(l.Id.Number, new Dictionary<int,PicsLink>());


                foreach (PicsLink p in _vPicsLink.PicsLinkList.Values)
                    _allLinkList[l.Id.Number].Add(p.Id.Number, p);

            }

            return _allLinkList;

        }
        
        public int SetUpVirtualNetwork(string _factors, Identification _vnoId)
        {
            VirtualNetwork _newVirtualNetwork = new VirtualNetwork(_vnoId);

            _network.AddVirtualNetwork(_newVirtualNetwork);

            int _vnId = _newVirtualNetwork.Id.Number;

            _VNoperators[_vnoId.Number].Networks.Add(_vnId, _newVirtualNetwork);

            string[] _factorsArray = _factors.Split(':');
            NetworkCoefficients _coefficients = new NetworkCoefficients(float.Parse(_factorsArray[0]), float.Parse(_factorsArray[1]), 
                float.Parse(_factorsArray[2]));

            _VNoperators[_vnoId.Number].Factors.Add(_vnId, _coefficients);

            _RC.EstimateNetworkElementsWeight(_coefficients, _vnId);

            return _vnId;
        }

        public void RemoveVirtualNetwork(int _vnId)
        {
            _RC.RemoveNetworkWeights(_vnId);
            _VNoperators[_network.GetVirtualNetwork(_vnId).VnoId.Number].Factors.Remove(_vnId);
            _VNoperators[_network.GetVirtualNetwork(_vnId).VnoId.Number].Networks.Remove(_vnId);

            Dictionary<int, Link> _linksToRemove = new Dictionary<int,Link>(_network.GetVirtualNetwork(_vnId).LinkList);

            foreach (Link l in _linksToRemove.Values)
                RemoveVirtualLink(_vnId, l.Id.Number, true);

            _network.RemoveVirtualNetwork(_vnId);

        }

        private PicsVirtualLink MapNewVirtualLink(int _srcMatrixId, int _dstMatrixId, LinkParameters _linkRequirements, int _vnId)
        {
            Identification _srcNodeId = _network.GetMatrix(_srcMatrixId).Id;
            Identification _dstNodeId = _network.GetMatrix(_dstMatrixId).Id;

            Identification _vnoId = _network.GetVirtualNetwork(_vnId).VnoId;

            PicsVirtualLink result = _RC.FindOptimalVirtualLink(_srcNodeId, _dstNodeId, _linkRequirements, _vnoId, _vnId);

            foreach(PicsLink l in result.PicsLinkList.Values)
                l.Parameters.AvailableCapacity -= _linkRequirements.Capacity;

            return result;
        }

        public Link AddVirtualLink(int _vnId, string _details)
        {
            int _vnoId = Network.GetVirtualNetwork(_vnId).VnoId.Number;
            _RC.EstimateNetworkElementsWeight(_VNoperators[_vnoId].Factors[_vnId], _vnId);
            string[] values = _details.Split(':');

            int _srcMatrixId = Int32.Parse(values[0]);
            int _dstMatrixId = Int32.Parse(values[1]);
            float _maxLinkDelay = Int32.Parse(values[2]);
            float _maxLinkErrorRate = float.Parse(values[3]);
            int _capacity = Int32.Parse(values[4]);

            /*
            foreach (PicsVirtualLink _pvlink in _network.VirtualLinkList.Values)
            {
                if (_srcMatrixId == _pvlink.SrcMatrix.Id.Number && _dstMatrixId == _pvlink.SinkMatrix.Id.Number
                    && _pvlink.vnId == _vnId && _capacity != _pvlink.Parameters.Capacity)
                {
                    RemoveVirtualLink(_vnId, _pvlink.LinkId);
                    break;
                }
            }
            */

            VirtualNetwork _currVirtualNetwork = _network.GetVirtualNetwork(_vnId);

            PicsMatrix _srcHostingMatrix = _network.GetMatrix(_srcMatrixId);
            PicsMatrix _dstHostingMatrix = _network.GetMatrix(_dstMatrixId);

            LinkParameters _linkRequirements = new LinkParameters(_capacity, _maxLinkDelay, _maxLinkErrorRate);

            PicsVirtualLink _newVirtualLink = MapNewVirtualLink(_srcMatrixId, _dstMatrixId, _linkRequirements, _vnId);

            Matrix _srcNode=null;
            bool _srcNodeAssigned = false;

            foreach (Matrix _matrix in _srcHostingMatrix.HostedMatrixList.Values)
            {
                if (_currVirtualNetwork.MatrixList.Keys.Contains(_matrix.Id.Number))
                {
                    _srcNode = _matrix;
                    _srcNodeAssigned = true;
                    break;
                }
            }

            Matrix _dstNode=null;
            bool _dstNodeAssigned = false;

            foreach (Matrix _matrix in _dstHostingMatrix.HostedMatrixList.Values)
            {
                if (_currVirtualNetwork.MatrixList.Keys.Contains(_matrix.Id.Number))
                {
                    _dstNode = _matrix;
                    _dstNodeAssigned = true;
                    break;
                }
            }

            if (!_srcNodeAssigned)
            {
                _srcNode = MapMatrixInformation(_srcHostingMatrix);

                _srcHostingMatrix.HostedMatrixList.Add(_srcNode.Id.Number, _srcNode);
                _currVirtualNetwork.AddMatrix(_srcNode);
            }

            if (!_dstNodeAssigned)
            {
                _dstNode = MapMatrixInformation(_dstHostingMatrix);

                _dstHostingMatrix.HostedMatrixList.Add(_dstNode.Id.Number, _dstNode);
                _currVirtualNetwork.AddMatrix(_dstNode);
            }

            //bool _linkExist = false;

            Link _newLink = null;

            /*foreach (Link _link in _currVirtualNetwork.LinkList.Values)
            {
                if (_link.SrcMatrix.Id.Number == _srcNode.Id.Number && _link.SinkMatrix.Id.Number == _dstNode.Id.Number)
                {
                    _link.Parameters = _newVirtualLink.Parameters;
                    _newLink = _link;
                    _linkExist = true;
                    break;
                }
            }
            */

            //if (!_linkExist)
            //{
                _newLink = new Link(_srcNode, _dstNode, _newVirtualLink.Parameters);
                _currVirtualNetwork.AddLink(_newLink);
            //}

            foreach (PicsLink _currPicsLink in _newVirtualLink.PicsLinkList.Values)
            {
                _currPicsLink.HostedLinkList.Add(_newLink.Id.Number, _newLink);
            }

            _srcNode.OutgoingLinkList.Add(_newLink.Id.Number, _newLink);

            _dstNode.IncomingLinkList.Add(_newLink.Id.Number, _newLink);

            _newVirtualLink.LinkId = _newLink.Id.Number;

            _network.LinkMappingList.Add(_newLink.Id.Number, _newVirtualLink.Id.Number);

            _network.VirtualLinkList.Add(_newVirtualLink.Id.Number, _newVirtualLink);

            return _newLink;
        }

        public void AddVirtualLink(PicsVirtualLink _picsVLinkOld)
        {
            VirtualNetwork _currVirtualNetwork = _network.GetVirtualNetwork(_picsVLinkOld.vnId);

            PicsMatrix _srcHostingMatrix = _network.GetMatrix(_picsVLinkOld.SrcMatrix.Id.Number);
            PicsMatrix _dstHostingMatrix = _network.GetMatrix(_picsVLinkOld.SinkMatrix.Id.Number);

            LinkParameters _linkRequirements = new LinkParameters(_picsVLinkOld.Requirements);

            PicsVirtualLink _newVirtualLink = MapNewVirtualLink(_srcHostingMatrix.Id.Number, _dstHostingMatrix.Id.Number, _linkRequirements, _currVirtualNetwork.Id.Number);

            Matrix _srcNode = null;
            bool _srcNodeAssigned = false;

            foreach (Matrix _matrix in _srcHostingMatrix.HostedMatrixList.Values)
            {
                if (_currVirtualNetwork.MatrixList.Keys.Contains(_matrix.Id.Number))
                {
                    _srcNode = _matrix;
                    _srcNodeAssigned = true;
                    break;
                }
            }

            Matrix _dstNode = null;
            bool _dstNodeAssigned = false;

            foreach (Matrix _matrix in _dstHostingMatrix.HostedMatrixList.Values)
            {
                if (_currVirtualNetwork.MatrixList.Keys.Contains(_matrix.Id.Number))
                {
                    _dstNode = _matrix;
                    _dstNodeAssigned = true;
                    break;
                }
            }

            if (!_srcNodeAssigned)
            {
                _srcNode = MapMatrixInformation(_srcHostingMatrix);

                _srcHostingMatrix.HostedMatrixList.Add(_srcNode.Id.Number, _srcNode);
                _currVirtualNetwork.AddMatrix(_srcNode);
            }

            if (!_dstNodeAssigned)
            {
                _dstNode = MapMatrixInformation(_dstHostingMatrix);

                _dstHostingMatrix.HostedMatrixList.Add(_dstNode.Id.Number, _dstNode);
                _currVirtualNetwork.AddMatrix(_dstNode);
            }

            Link _newLink = null;

            _newLink = new Link(_srcNode, _dstNode, _newVirtualLink.Parameters);
            _currVirtualNetwork.AddLink(_newLink);

            foreach (PicsLink _currPicsLink in _newVirtualLink.PicsLinkList.Values)
            {
                _currPicsLink.HostedLinkList.Add(_newLink.Id.Number, _newLink);
            }

            _srcNode.OutgoingLinkList.Add(_newLink.Id.Number, _newLink);

            _dstNode.IncomingLinkList.Add(_newLink.Id.Number, _newLink);

            _newVirtualLink.LinkId = _newLink.Id.Number;

            _network.LinkMappingList.Add(_newLink.Id.Number, _newVirtualLink.Id.Number);

            _network.VirtualLinkList.Add(_newVirtualLink.Id.Number, _newVirtualLink);
        }

        public void RemoveVirtualLink(int _vnId, int _vlinkId, bool _disconnect)
        {
            VirtualNetwork _vNetwork = _network.GetVirtualNetwork(_vnId);
            List<int> _oldLinkConnections =
                new List<int>(_vNetwork.GetLink(_vlinkId).LinkConnectionsList.Keys);
            int _nc;

            if(_disconnect)
            {
                foreach (int lc in _oldLinkConnections)
                {
                    _nc = _vNetwork.GetLink(_vlinkId).LinkConnectionsList[lc].NetworkConnectionId.Number;
                    SNCC.TearDownNetworkConnection(_vnId, _nc);
                }
            }

            int _srcId = _vNetwork.GetLink(_vlinkId).SrcMatrix.Id.Number;
            int _dstId = _vNetwork.GetLink(_vlinkId).SinkMatrix.Id.Number;

            Matrix _srcMatrix = _vNetwork.GetLink(_vlinkId).SrcMatrix;
            Matrix _dstMatrix = _vNetwork.GetLink(_vlinkId).SinkMatrix;

            _vNetwork.LinkList.Remove(_vlinkId);

            /*
            bool _srcNeeded = false;
            bool _dstNeeded = false;

            foreach (Link _vlink in _vNetwork.LinkList.Values)
            {
                if (_vlink.SrcMatrix.Id.Number == _srcId || _vlink.SinkMatrix.Id.Number == _srcId)
                    _srcNeeded = true;

                if (_vlink.SrcMatrix.Id.Number == _dstId || _vlink.SinkMatrix.Id.Number == _dstId)
                    _dstNeeded = true;
            }

             */

            PicsMatrix _srcHostingMatrix = null;
            PicsMatrix _dstHostingMatrix = null;

            foreach (PicsMatrix _node in _network.MatrixResourceList.Values)
            {
                if (_node.Name == _srcMatrix.Name)
                    _srcHostingMatrix = _node;

                if (_node.Name == _dstMatrix.Name)
                    _dstHostingMatrix = _node;
            }


            _srcMatrix.OutgoingLinkList.Remove(_vlinkId);
            _dstMatrix.IncomingLinkList.Remove(_vlinkId);
            
            if (_srcMatrix.OutgoingLinkList.Count == 0 && _srcMatrix.IncomingLinkList.Count == 0)
            {
                _vNetwork.MatrixList.Remove(_srcId);
                _srcHostingMatrix.HostedMatrixList.Remove(_srcId);
            }

            if (_dstMatrix.OutgoingLinkList.Count == 0 && _dstMatrix.IncomingLinkList.Count == 0)
            {
                _vNetwork.MatrixList.Remove(_dstId);
                _dstHostingMatrix.HostedMatrixList.Remove(_dstId);
            }   

            int _picsVLinkId = _network.LinkMappingList[_vlinkId];
            PicsVirtualLink _picsVLink = _network.VirtualLinkList[_picsVLinkId];

            /*
            foreach (PicsVirtualLink _picsLink in _network.VirtualLinkList.Values)
            {
                if (_picsLink.SrcMatrix.Id.Number == _srcHostingMatrix.Id.Number
                    && _picsLink.SinkMatrix.Id.Number == _dstHostingMatrix.Id.Number)
                    _picsVLink = _picsLink;
            }
            */

            foreach (PicsLink _plink in _picsVLink.PicsLinkList.Values)
            {
                _plink.HostedLinkList.Remove(_vlinkId);
                _plink.Parameters.AvailableCapacity += _picsVLink.Requirements.Capacity;
            }

            _network.VirtualLinkList.Remove(_picsVLink.Id.Number);
            _network.LinkMappingList.Remove(_vlinkId);
        }

        public Matrix MapMatrixInformation(PicsMatrix _realMatrix)
        {
            Matrix vMatrix = new Matrix();

            vMatrix.Name = _realMatrix.Name;
            vMatrix.PositionX = _realMatrix.PositionX;
            vMatrix.PositionY = _realMatrix.PositionY;
            vMatrix.Type = _realMatrix.Type;

            return vMatrix;
        }
        /*
        public VirtualNetwork SetUpVirtualNetwork(Identification _vnoId, List<PicsVirtualLink> _virtualLinkList)
        {
            VirtualNetwork _newVirtualNetwork = new VirtualNetwork(_vnoId);

            List<string> _mappedNodes = new List<string>();

            // loop over every virtual link in a given network
            foreach (PicsVirtualLink _vlink in _virtualLinkList)
            {
                Matrix _srcNode = new Matrix();
                Matrix _sinkNode = new Matrix();

                // check if the source and sink nodes of the link don't already exist in new virtual network, if not, add them to it
                // (also providing information about them to real physical nodes) 
                if(!_mappedNodes.Contains(_vlink.SrcMatrix.Id.Number))
                {
                    _network.GetMatrix(_vlink.SrcMatrix.Id.Number).HostedMatrixList.Add(_srcNode.Id.Number, _srcNode);
                    _newVirtualNetwork.AddMatrix(_srcNode);

                    _mappedNodes.Add(_vlink.SrcMatrix.Id.Number);
                }
                if(!_mappedNodes.Contains(_vlink.SinkMatrix.Id.Number))
                {
                    _network.GetMatrix(_vlink.SinkMatrix.Id.Number).HostedMatrixList.Add(_sinkNode.Id.Number, _sinkNode);
                    _newVirtualNetwork.AddMatrix(_sinkNode);
                
                    _mappedNodes.Add(_vlink.SinkMatrix.Id.Number);
                }

                Link _newLink = new Link(_srcNode, _sinkNode, new LinkParameters(_vlink.Parameters.Capacity));

                _newVirtualNetwork.AddLink(_newLink);

                // add information about this virtual link to every physical link that is creating it
                foreach (PicsLink _currPicsLink in _vlink.PicsLinkList.Values)
                {
                    _currPicsLink.HostedLinkList.Add(_newLink.Id.Number, _newLink);
                }

                _srcNode.OutgoingLinkList.Add(_newLink.Id.Number, _newLink);

                _sinkNode.IncomingLinkList.Add(_newLink.Id.Number, _newLink);

            }

            return _newVirtualNetwork;

        }
        
        public List<Route> GetVNRequest(List<VirtualLink> _virtualLinks, string _VNOid, NetworkCoefficients _VNFactors, LinkParameters _maxAdditiveParameters)
        {
            List<Route> _VNRoutes = new List<Route>();
            Route _currentRoute;

            foreach (Link _link in _network.Links)
            {
                _link.Weight = _VNFactors.LinkDelay * _link.Parameters.Delay + _VNFactors.LinkErrorRate * _link.Parameters.ErrorRate;
            }
            foreach (KeyValuePair<string, SwitchingMatrix> _kvp in _network.Nodes)
                _kvp.Value.Weight = _VNFactors.NodeDelay * _kvp.Value.Parameters.Delay;

            foreach (VirtualLink _vlink in _virtualLinks)
            {
                List<Link> _validLinks = new List<Link>();

                foreach (Link _link in _network.Links)
                {
                    if (_link.Parameters.Delay <= _vlink.Requirements.Delay &&
                        _link.Parameters.ErrorRate <= _vlink.Requirements.ErrorRate &&
                        _link.BandwidthAvailable >= _vlink.Requirements.Bandwidth)
                        _validLinks.Add(_link);
                }

                _currentRoute = _network.FindOptimalRoute(_vlink.EndA, _vlink.EndB,_validLinks, _maxAdditiveParameters);

                foreach (Link _routeLink in _currentRoute.Links)
                {
                    foreach (Link _link in _network.Links)
                    {
                        if (_link.Id == _routeLink.Id)
                        {
                            _link.BandwidthAvailable -= _vlink.Requirements.Bandwidth;
                            break;
                        }
                    }

                    _routeLink.Parameters.Bandwidth = _vlink.Requirements.Bandwidth;
                }

                _VNRoutes.Add(_currentRoute);

            }

            if (_VNoperators.ContainsKey(_VNOid))
            {
                _VNoperators[_VNOid].Networks.Add(_VNRoutes);
                _VNoperators[_VNOid].Factors.Add(_VNFactors);
            }
            else
            {
                _VNoperators.Add(_VNOid, new VirtualNetworkOperatorInfo(_VNOid));
                _VNoperators[_VNOid].Networks.Add(_VNRoutes);
                _VNoperators[_VNOid].Factors.Add(_VNFactors);
            }

            return _VNRoutes;
        }
        */

    }
}
